#ifndef _core_switch_detail_value_at_key_impl_hpp_
#define _core_switch_detail_value_at_key_impl_hpp_

/** \file value_at_key_impl.hpp
 *  \author Adrian Schweizer
 *  \created  $Mo 11 Feb 08:33:01 pm CET 2008 schwadri@SchwadriLaptop.local$
 *  \modified $Mo 11 Feb 08:40:14 pm CET 2008 schwadri@SchwadriLaptop.local$
 */

#include <boost/mpl/find.hpp>
#include <boost/mpl/is_sequence.hpp>
#include <boost/mpl/end.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/void.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/type_traits/is_same.hpp>

#include "case_invoker.hpp"

namespace core {

    struct case_tag;

}

namespace boost {
    namespace mpl {
        template <typename Tag0,typename Tag1>
            struct equal_to_impl;

        template <typename Tag>
            struct equal_to_impl<Tag,core::default_label_tag>
            {
                template <typename X, typename Y>
                    struct apply;

                template <typename X>
                    struct apply<X,core::default_label>
                    :   false_
                    { };
            };

        template <typename Tag>
            struct equal_to_impl<core::default_label_tag,Tag>
            {
                template <typename X, typename Y>
                    struct apply;

                template <typename Y>
                    struct apply<core::default_label,Y>
                    :   false_
                    { };
            };

        template <>
            struct equal_to_impl<core::default_label_tag,core::default_label_tag>
            {
                template <typename X, typename Y>
                    struct apply
                    :   true_
                    { };
            };

    }

    namespace fusion { namespace extension {

    template <typename Tag>
        struct value_at_key_impl;

    template <>
        struct value_at_key_impl<core::case_tag>
        {

            template <typename Case, typename key, std::size_t offset>
                struct  find_key;

            struct compare_label
            {
                template <typename Case, typename Key, std::size_t Offset>
                    struct apply
                    :   mpl::eval_if<
                            is_same<typename Case::labels, Key>,
                            mpl::identity<core::case_invoker<Case,Key,mpl::size_t<Offset> > >,
                            find_key<typename Case::next,Key,Offset+1>
                        >
                    { };
            };

            struct search_label
            {

                template <typename Case, typename Key, std::size_t Offset>
                    struct apply
                    {
                        typedef typename mpl::find_if<typename Case::labels,mpl::equal_to<mpl::placeholders::_,Key> >::type liter;
                        typedef typename
                            mpl::eval_if<
                                is_same
                                <
                                    liter,
                                    typename mpl::end<typename Case::labels>::type
                                >,
                                find_key<typename Case::next,Key,Offset+1>,
                                mpl::identity<core::case_invoker<Case,typename mpl::deref<liter>::type,mpl::size_t<Offset> > >

                            >::type     type;
                    };
            };

            template <typename Case, typename Key, std::size_t Offset>
                struct  find_key
                :   mpl::if_<
                        mpl::is_sequence<typename Case::labels>,
                        search_label,
                        compare_label
                    >::type::template apply<Case,Key,Offset>
                { };

            template <typename Key, std::size_t Offset>
                struct  find_key<mpl::void_, Key, Offset>
                :   mpl::void_
                { };

            template <typename Case, typename Key>
                struct apply
                :   find_key<Case,Key,0>
                { };
        };

}}}

#endif // _core_switch_detail_value_at_key_impl_hpp_
